package player

import (
	"context"
	"gitee.com/xcy871622040/gameframe/cluster"
	"github.com/go-redis/redis/v8"
	"sync"
)

type MemoryItem struct {
	lock sync.RWMutex
	data map[string]string
}

func (mem *MemoryItem) Get(key string) string {
	mem.lock.RLock()
	defer mem.lock.RUnlock()

	return mem.data[key]
}

func (mem *MemoryItem) Set(key, val string) {
	mem.lock.Lock()
	defer mem.lock.Unlock()

	mem.data[key] = val
}

func (mem *MemoryItem) Del(key string) {
	mem.lock.Lock()
	defer mem.lock.Unlock()

	delete(mem.data, key)
}

func (mem *MemoryItem) Range(eval func(key, val string) bool) {
	mem.lock.Lock()
	defer mem.lock.Unlock()
	for key, val := range mem.data {
		if !eval(key, val) {
			return
		}
	}
}

// MemoryAllocator
// 基于内存的服务器分配管理器
type MemoryAllocator struct {
	userData *sync.Map
}

func NewMemoryAllocator() cluster.Allocator {
	return &MemoryAllocator{
		userData: &sync.Map{}, // uid -> MemoryItem
	}
}

// GetAll 获取玩家的所有服务器配置
func (m *MemoryAllocator) GetAll(_ context.Context, uid string) (res map[string]string) {
	if val, ok := m.userData.Load(uid); ok {
		val.(*MemoryItem).Range(func(key, val string) bool {
			res[key] = val
			return true
		})
	}
	return
}

// GetMulti 获取玩家的多个服务器分配情况
func (m *MemoryAllocator) GetMulti(_ context.Context, uid string, serverTypes []string) (res map[string]string) {
	if val, ok := m.userData.Load(uid); ok {
		dis := val.(*MemoryItem)
		for index := range serverTypes {
			res[serverTypes[index]] = dis.Get(serverTypes[index])
		}
	}
	return
}

// SetMulti 为玩家分配服务器，并保证在服务器上有玩家状态存储时，会一直分配这个服务器给此玩家
func (m *MemoryAllocator) SetMulti(_ context.Context, uid string, servers map[string]string) error {
	if val, ok := m.userData.Load(uid); ok {
		dis := val.(*MemoryItem)
		for k, v := range servers {
			dis.Set(k, v)
		}
	} else {
		m.userData.Store(uid, &MemoryItem{
			data: servers,
			lock: sync.RWMutex{},
		})
	}
	return nil
}

// GetServer 获得一个人的一个服务器情况
func (m *MemoryAllocator) GetServer(_ context.Context, uid string, serverType string) string {
	if val, ok := m.userData.Load(uid); ok {
		return val.(*MemoryItem).Get(serverType)
	}
	return ""
}

// GetUsersServer 获得多个人的多个所在服务器数据
func (m *MemoryAllocator) GetUsersServer(_ context.Context, uids []string, serverType string) (res map[string]string) {
	for index := range uids {
		uid := uids[index]
		if val, ok := m.userData.Load(uid); ok {
			res[uid] = val.(*MemoryItem).Get(serverType)
		}
	}
	return
}

// SetUsersServer 存入多个玩家在一个服务器的数据，在玩家进入或离开服务器时调用
func (m *MemoryAllocator) SetUsersServer(_ context.Context, uids []string, serverType string, serverId string) error {
	for index := range uids {
		uid := uids[index]
		if val, ok := m.userData.Load(uid); ok {
			val.(*MemoryItem).Set(serverType, serverId)
		} else {
			m.userData.Store(uid, &MemoryItem{
				lock: sync.RWMutex{},
				data: map[string]string{serverType: serverId},
			})
		}
	}
	return nil
}

// UnsetUsersServer 清除玩家一类服务器
func (m *MemoryAllocator) UnsetUsersServer(_ context.Context, uids []string, serverType string) error {
	for index := range uids {
		uid := uids[index]
		if val, ok := m.userData.Load(uid); ok {
			val.(*MemoryItem).Del(serverType)
		}
	}
	return nil
}

// UnAllocate 取消服务器分配，在服务器清除玩家状态时，取消分配给玩家的服务器，eg. 从 redis 中删除记录
func (m *MemoryAllocator) UnAllocate(_ context.Context, uid string) error {
	m.userData.Delete(uid)
	return nil
}

// RedisAllocator
// 使用redis作为分配器数据存储
type RedisAllocator struct {
	client *redis.Client
}

func NewRedisAllocator(cli *redis.Client) cluster.Allocator {
	return &RedisAllocator{
		client: cli,
	}
}

// SetMulti 设置玩家服务器分配信息
func (r *RedisAllocator) SetMulti(ctx context.Context, uid string, servers map[string]string) error {
	return r.client.HMSet(ctx, uid, servers).Err()
}

// SetUsersServer 设置多个玩家某一类服务器分配细信息
func (r *RedisAllocator) SetUsersServer(ctx context.Context, uids []string, serverType string, serverId string) error {
	pipe := r.client.Pipeline()
	for idx := range uids {
		if err := pipe.HMSet(ctx, uids[idx], serverType, serverId).Err(); err != nil {
			return err
		}
	}
	return nil
}

// GetAll 获取玩家的所有服务器配置
func (r *RedisAllocator) GetAll(ctx context.Context, uid string) map[string]string {
	return r.client.HGetAll(ctx, uid).Val()
}

// GetServer 获得一个人的一个服务器信息
func (r *RedisAllocator) GetServer(ctx context.Context, uid string, serverType string) string {
	return r.client.HMGet(ctx, uid, serverType).Val()[0].(string)
}

// GetMulti 获取玩家的多个服务器信息
func (r *RedisAllocator) GetMulti(ctx context.Context, uid string, serverTypes []string) map[string]string {
	var result = make(map[string]string)
	stall := r.client.HGetAll(ctx, uid).Val()
	for i := range serverTypes {
		result[serverTypes[i]] = stall[serverTypes[i]]
	}
	return result
}

// GetUsersServer 获得多个玩家某类服务器信息
func (r *RedisAllocator) GetUsersServer(ctx context.Context, uids []string, serverType string) map[string]string {
	pipe := r.client.Pipeline()
	var result = make(map[string]string)

	for i := range uids {
		result[uids[i]] = pipe.HMGet(ctx, uids[i], serverType).Val()[0].(string)
	}
	return result
}

// UnAllocate 清除玩家服务器信息
func (r *RedisAllocator) UnAllocate(ctx context.Context, uid string) error {
	return r.client.Del(ctx, uid).Err()
}

// UnsetUsersServer 清除多个玩家一类服务器信息
func (r *RedisAllocator) UnsetUsersServer(ctx context.Context, uids []string, serverType string) error {
	pipe := r.client.Pipeline()
	for i := range uids {
		if err := pipe.HDel(ctx, uids[i], serverType).Err(); err != nil {
			return err
		}
	}
	return nil
}
